﻿namespace Neovolve.Toolkit.UnitTests.Instrumentation
{
    using System;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Neovolve.Toolkit.Instrumentation;
    using Neovolve.Toolkit.Storage;
    using Rhino.Mocks;

    /// <summary>
    /// This is a test class for CacheResolverTests and is intended
    ///   to contain all CacheResolverTests Unit Tests.
    /// </summary>
    [TestClass]
    public class CacheResolverTests
    {
        /// <summary>
        /// Runs test for can create with resolver.
        /// </summary>
        [TestMethod]
        [Description("Black box tests")]
        public void CanCreateWithResolverTest()
        {
            MockRepository mock = new MockRepository();
            ITraceSourceResolver resolver = mock.Stub<ITraceSourceResolver>();
            CacheResolver target = new CacheResolver(resolver);

            Assert.AreEqual(resolver, target.ChildResolver, "ChildResolver returned an incorrect value");
        }

        /// <summary>
        /// Runs test for creating with null resolver throws exception.
        /// </summary>
        [TestMethod]
        [Description("Black box tests")]
        [ExpectedException(typeof(ArgumentNullException))]
        public void CreatingWithNullResolverThrowsExceptionTest()
        {
            new CacheResolver(null);
        }

        /// <summary>
        /// A test for ResolveNames.
        /// </summary>
        [TestMethod]
        [Description("White box tests")]
        public void GetTraceSourceNamesCacheTest()
        {
            MockRepository mock = new MockRepository();
            ITraceSourceResolver resolver = mock.StrictMock<ITraceSourceResolver>();
            CacheResolver_Accessor target = new CacheResolver_Accessor(resolver);
            ICacheStore traceCache = CacheStoreFactory.Create();

            traceCache.Clear();

            String cacheKey = target.GenerateNamesCacheKey();

            Assert.IsNotNull(traceCache, "Failed to obtain reference to the cache");
            Assert.IsNull(traceCache[cacheKey], "Names were not cleared from the cache");

            Collection<String> expected = new Collection<String>
                                          {
                                              "test", 
                                              "more"
                                          };

            using (mock.Record())
            {
                resolver.ResolveNames();

                LastCall.Return(expected);
            }

            using (mock.Playback())
            {
                Collection<String> actual = target.ResolveNames();

                Assert.IsNotNull(traceCache[cacheKey], "Names were not stored the cache");
                Assert.AreEqual(expected, traceCache[cacheKey], "An incorrect value was stored in the cache");
                Assert.AreEqual(expected, actual, "ResolveNames returned an incorrect value");

                // Make the call again to ensure that the cache is referenced rather than the resolver for the second call
                actual = target.ResolveNames();

                Assert.IsNotNull(traceCache[cacheKey], "Names were not stored in the cache");
                Assert.AreEqual(expected, traceCache[cacheKey], "An incorrect value was stored in the cache");
                Assert.AreEqual(expected, actual, "ResolveNames returned an incorrect value");
            }
        }

        /// <summary>
        /// A test for Reload.
        /// </summary>
        [TestMethod]
        [Description("White box tests")]
        public void ReloadEmptyKeyTest()
        {
            MockRepository mock = new MockRepository();
            ITraceSourceResolver resolver = mock.StrictMock<ITraceSourceResolver>();
            CacheResolver_Accessor target = new CacheResolver_Accessor(resolver);
            ICacheStore traceCache = CacheStoreFactory.Create();

            traceCache[String.Empty] = "Some random value";

            target.Reload();
        }

        /// <summary>
        /// A test for Reload.
        /// </summary>
        [TestMethod]
        [Description("White box tests")]
        public void ReloadTest()
        {
            MockRepository mock = new MockRepository();
            ITraceSourceResolver resolver = mock.StrictMock<ITraceSourceResolver>();
            CacheResolver_Accessor target = new CacheResolver_Accessor(resolver);
            String namesCacheKey = target.GenerateNamesCacheKey();
            ICacheStore traceCache = CacheStoreFactory.Create();

            traceCache.Clear();

            Collection<String> expectedNames = new Collection<String>
                                               {
                                                   "test", 
                                                   "more"
                                               };

            TraceSource testSource = new TraceSource("test");
            TraceSource moreSource = new TraceSource("more");

            using (mock.Record())
            {
                resolver.ResolveNames();
                LastCall.Return(expectedNames);

                resolver.Resolve("test", StringComparison.OrdinalIgnoreCase);
                LastCall.Return(testSource);

                resolver.Resolve("more", StringComparison.OrdinalIgnoreCase);
                LastCall.Return(moreSource);

                resolver.Reload();
            }

            using (mock.Playback())
            {
                Collection<String> actualNames = target.ResolveNames();

                Assert.IsNotNull(traceCache[namesCacheKey], "Names were not cleared from the cache");
                Assert.AreEqual(expectedNames, traceCache[namesCacheKey], "An incorrect value was returned from the cache");
                Assert.AreEqual(expectedNames, actualNames, "ResolveNames returned an incorrect value");

                TraceSource result = target.Resolve("test", StringComparison.OrdinalIgnoreCase);
                String resultCacheKey = target.GenerateTraceSourceCacheKey("test");

                Assert.IsNotNull(result, "Resolve failed to return a value");
                Assert.IsNotNull(traceCache[resultCacheKey], "The trace source is not in the cache");
                Assert.AreEqual(testSource, result, "Resolve returned an incorrect result");

                result = target.Resolve("more", StringComparison.OrdinalIgnoreCase);
                resultCacheKey = target.GenerateTraceSourceCacheKey("more");

                Assert.IsNotNull(result, "Resolve failed to return a value");
                Assert.IsNotNull(traceCache[resultCacheKey], "The trace source is not in the cache");
                Assert.AreEqual(moreSource, result, "Resolve returned an incorrect result");

                target.Reload();

                // Test the cache for the keys
                Assert.IsNull(traceCache[namesCacheKey], "Names were not cleared from the cache");

                resultCacheKey = target.GenerateTraceSourceCacheKey("test");
                Assert.IsNull(traceCache[resultCacheKey], "The trace source is not cleared from the cache");

                resultCacheKey = target.GenerateTraceSourceCacheKey("more");
                Assert.IsNull(traceCache[resultCacheKey], "The trace source is not cleared from the cache");
            }
        }

        /// <summary>
        /// A test for Resolve.
        /// </summary>
        [TestMethod]
        [Description("White box tests")]
        public void ResolveStringNullResolverResponseTest()
        {
            MockRepository mock = new MockRepository();
            ITraceSourceResolver resolver = mock.StrictMock<ITraceSourceResolver>();
            CacheResolver_Accessor target = new CacheResolver_Accessor(resolver);
            ICacheStore traceCache = CacheStoreFactory.Create();

            traceCache.Clear();

            using (mock.Record())
            {
                resolver.Resolve("TEST", StringComparison.OrdinalIgnoreCase);
                LastCall.Return(null);
            }

            using (mock.Playback())
            {
                TraceSource result = target.Resolve("TEST", StringComparison.OrdinalIgnoreCase);
                String resultCacheKey = target.GenerateTraceSourceCacheKey("test");

                Assert.IsNull(result, "Resolve failed to return a value");
                Assert.IsNull(traceCache[resultCacheKey], "The trace source is not in the cache");
            }
        }

        /// <summary>
        /// A test for Resolve.
        /// </summary>
        [TestMethod]
        [Description("White box tests")]
        public void ResolveStringTest()
        {
            MockRepository mock = new MockRepository();
            ITraceSourceResolver resolver = mock.StrictMock<ITraceSourceResolver>();
            CacheResolver_Accessor target = new CacheResolver_Accessor(resolver);
            String namesCacheKey = target.GenerateNamesCacheKey();
            ICacheStore traceCache = CacheStoreFactory.Create();

            traceCache.Clear();

            Collection<String> expectedNames = new Collection<String>
                                               {
                                                   "test", 
                                                   "more"
                                               };

            TraceSource testSource = new TraceSource("test");
            TraceSource moreSource = new TraceSource("more");

            using (mock.Record())
            {
                resolver.ResolveNames();
                LastCall.Return(expectedNames);

                resolver.Resolve("TEST", StringComparison.OrdinalIgnoreCase);
                LastCall.Return(testSource);

                resolver.Resolve("More", StringComparison.OrdinalIgnoreCase);
                LastCall.Return(moreSource);
            }

            using (mock.Playback())
            {
                Collection<String> actualNames = target.ResolveNames();

                Assert.IsNotNull(traceCache[namesCacheKey], "Names were not cleared from the cache");
                Assert.AreEqual(expectedNames, traceCache[namesCacheKey], "An incorrect value was returned from the cache");
                Assert.AreEqual(expectedNames, actualNames, "ResolveNames returned an incorrect value");

                TraceSource result = target.Resolve("TEST", StringComparison.OrdinalIgnoreCase);
                String resultCacheKey = target.GenerateTraceSourceCacheKey("test");

                Assert.IsNotNull(result, "Resolve failed to return a value");
                Assert.IsNotNull(traceCache[resultCacheKey], "The trace source is not in the cache");
                Assert.AreEqual(testSource, result, "Resolve returned an incorrect result");

                // Resolve this item again to test that it is returned from the cache successfully
                result = target.Resolve("TEST", StringComparison.OrdinalIgnoreCase);
                resultCacheKey = target.GenerateTraceSourceCacheKey("test");

                Assert.IsNotNull(result, "Resolve failed to return a value");
                Assert.IsNotNull(traceCache[resultCacheKey], "The trace source is not in the cache");
                Assert.AreEqual(testSource, result, "Resolve returned an incorrect result");

                result = target.Resolve("More", StringComparison.OrdinalIgnoreCase);
                resultCacheKey = target.GenerateTraceSourceCacheKey("more");

                Assert.IsNotNull(result, "Resolve failed to return a value");
                Assert.IsNotNull(traceCache[resultCacheKey], "The trace source is not in the cache");
                Assert.AreEqual(moreSource, result, "Resolve returned an incorrect result");
            }
        }

        /// <summary>
        /// Runs test for resolve with empty trace source name throws exception.
        /// </summary>
        [TestMethod]
        [Description("Black box tests")]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ResolveWithEmptyTraceSourceNameThrowsExceptionTest()
        {
            CacheResolver target = new CacheResolver(new TestTraceSourceResolver());

            target.Resolve(String.Empty, StringComparison.OrdinalIgnoreCase);
        }

        /// <summary>
        /// Runs test for resolve with null trace source name throws exception.
        /// </summary>
        [TestMethod]
        [Description("Black box tests")]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ResolveWithNullTraceSourceNameThrowsExceptionTest()
        {
            CacheResolver target = new CacheResolver(new TestTraceSourceResolver());

            target.Resolve(null, StringComparison.OrdinalIgnoreCase);
        }

        /// <summary>
        /// Traces the source name different resolver cache test.
        /// </summary>
        [TestMethod]
        [Description("White box tests")]
        public void TraceSourceNamesDifferentResolverCacheTest()
        {
            MockRepository mock = new MockRepository();

            // Create the first ChildResolver and resolver
            ITraceSourceResolver firstResolver = mock.StrictMock<ITraceSourceResolver>();
            CacheResolver_Accessor firstTarget = new CacheResolver_Accessor(firstResolver);
            ICacheStore firstTraceCache = CacheStoreFactory.Create();
            String firstNamesCacheKey = firstTarget.GenerateNamesCacheKey();
            Collection<String> firstExpectedNames = new Collection<String>
                                                    {
                                                        "first"
                                                    };

            // Create the second ChildResolver and resolver
            ITraceSourceResolver secondResolver = mock.StrictMultiMock<ITraceSourceResolver>(typeof(ICloneable));
            CacheResolver_Accessor secondTarget = new CacheResolver_Accessor(secondResolver);
            ICacheStore secondTraceCache = CacheStoreFactory.Create();
            String secondNamesCacheKey = secondTarget.GenerateNamesCacheKey();
            Collection<String> secondExpectedNames = new Collection<String>
                                                     {
                                                         "second"
                                                     };

            Assert.AreNotEqual(
                firstResolver.GetType().AssemblyQualifiedName, secondResolver.GetType().AssemblyQualifiedName, "Resolvers have the same name");

            // Clear the caches
            firstTraceCache.Clear();
            secondTraceCache.Clear();

            using (mock.Record())
            {
                firstResolver.ResolveNames();
                LastCall.Return(firstExpectedNames);

                secondResolver.ResolveNames();
                LastCall.Return(secondExpectedNames);
            }

            using (mock.Playback())
            {
                Collection<String> firstResult = firstTarget.ResolveNames();

                Assert.IsNotNull(firstTraceCache[firstNamesCacheKey], "Names were not cleared from the cache");
                Assert.AreEqual(firstExpectedNames, firstTraceCache[firstNamesCacheKey], "An incorrect value was returned from the cache");
                Assert.AreEqual(firstExpectedNames, firstResult, "ResolveNames returned an incorrect value");

                Collection<String> secondResult = secondTarget.ResolveNames();

                Assert.IsNotNull(secondTraceCache[secondNamesCacheKey], "Names were not cleared from the cache");
                Assert.AreEqual(secondExpectedNames, secondTraceCache[secondNamesCacheKey], "An incorrect value was returned from the cache");
                Assert.AreEqual(secondExpectedNames, secondResult, "ResolveNames returned an incorrect value");

                Assert.AreNotEqual(firstResult, secondResult, "Resolvers returned the same names");
            }
        }

        /// <summary>
        /// Traces the sources different resolver cache test.
        /// </summary>
        [TestMethod]
        [Description("White box tests")]
        public void TraceSourcesDifferentResolverCacheTest()
        {
            // Create the first ChildResolver and resolver
            MockRepository mock = new MockRepository();
            ITraceSourceResolver firstResolver = mock.StrictMock<ITraceSourceResolver>();
            CacheResolver_Accessor firstTarget = new CacheResolver_Accessor(firstResolver);
            ICacheStore firstTraceCache = CacheStoreFactory.Create();
            TraceSource firstSource = new TraceSource("first");

            // Create the second ChildResolver and resolver
            ITraceSourceResolver secondResolver = mock.StrictMultiMock<ITraceSourceResolver>(typeof(ICloneable));
            CacheResolver_Accessor secondTarget = new CacheResolver_Accessor(secondResolver);
            ICacheStore secondTraceCache = CacheStoreFactory.Create();
            TraceSource secondSource = new TraceSource("second");

            Assert.AreNotEqual(
                firstResolver.GetType().AssemblyQualifiedName, secondResolver.GetType().AssemblyQualifiedName, "Resolvers have the same name");

            // Clear the caches
            firstTraceCache.Clear();
            secondTraceCache.Clear();

            using (mock.Record())
            {
                firstResolver.Resolve("first", StringComparison.OrdinalIgnoreCase);
                LastCall.Return(firstSource);

                secondResolver.Resolve("second", StringComparison.OrdinalIgnoreCase);
                LastCall.Return(secondSource);
            }

            using (mock.Playback())
            {
                TraceSource firstResult = firstTarget.Resolve("first", StringComparison.OrdinalIgnoreCase);
                String firstResultCacheKey = firstTarget.GenerateTraceSourceCacheKey("first");

                Assert.IsNotNull(firstResult, "Resolve failed to return a value");
                Assert.IsNotNull(firstTraceCache[firstResultCacheKey], "The trace source is not in the cache");
                Assert.AreEqual(firstSource, firstResult, "Resolve returned an incorrect result");

                TraceSource secondResult = secondTarget.Resolve("second", StringComparison.OrdinalIgnoreCase);
                String secondResultCacheKey = secondTarget.GenerateTraceSourceCacheKey("second");

                Assert.IsNotNull(secondResult, "Resolve failed to return a value");
                Assert.IsNotNull(secondTraceCache[secondResultCacheKey], "The trace source is not in the cache");
                Assert.AreEqual(secondSource, secondResult, "Resolve returned an incorrect result");

                Assert.AreNotEqual(firstResult, secondResult, "Resolvers returned the same sources");
            }
        }

        /// <summary>
        /// Gets or sets the test context which provides
        ///   information about and functionality for the current test run.
        /// </summary>
        /// <value>
        /// The test context.
        /// </value>
        public TestContext TestContext
        {
            get;
            set;
        }
    }
}